home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectInput / DIConfig / flexlistbox.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  13.5 KB  |  593 lines

  1. //-----------------------------------------------------------------------------
  2. // File: flexlistbox.cpp
  3. //
  4. // Desc: Implements a list box control that can display a list of text strings,
  5. //       each can be selected by mouse.  The class CFlexListBox is derived from
  6. //       CFlexWnd.  It is used by the class CFlexComboBox when it needs to
  7. //       expand to show the list of choices.
  8. //
  9. // Copyright (C) 1999-2001 Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11.  
  12. #include "common.hpp"
  13.  
  14.  
  15. CFlexListBox::CFlexListBox() :
  16.     m_nTextHeight(-1),
  17.     m_hWndNotify(NULL),
  18.     m_rgbText(RGB(255,255,255)),
  19.     m_rgbBk(RGB(0,0,0)),
  20.     m_rgbSelText(RGB(0,0,255)),
  21.     m_rgbSelBk(RGB(0,0,0)),
  22.     m_rgbFill(RGB(0,0,255)),
  23.     m_rgbLine(RGB(0,255,255)),
  24.     m_dwFlags(0),
  25.     m_nTopIndex(0),
  26.     m_nSBWidth(11),
  27.     m_hFont(NULL),
  28.     m_bOpenClick(FALSE),
  29.     m_bDragging(FALSE),
  30.     m_bCapture(FALSE),
  31.     m_nSelItem(-1),
  32.     m_bVertSB(FALSE)
  33. {
  34. }
  35.  
  36. CFlexListBox::~CFlexListBox()
  37. {
  38. }
  39.  
  40. CFlexListBox *CreateFlexListBox(FLEXLISTBOXCREATESTRUCT *pcs)
  41. {
  42.     CFlexListBox *psb = new CFlexListBox;
  43.  
  44.     if (psb && psb->Create(pcs))
  45.         return psb;
  46.     
  47.     delete psb;
  48.     return NULL;
  49. }
  50.  
  51. BOOL CFlexListBox::CreateForSingleSel(FLEXLISTBOXCREATESTRUCT *pcs)
  52. {
  53.     if (!Create(pcs))
  54.         return FALSE;
  55.  
  56.     StartSel();
  57.  
  58.     return TRUE;
  59. }
  60.  
  61. BOOL CFlexListBox::Create(FLEXLISTBOXCREATESTRUCT *pcs)
  62. {
  63.     if (this == NULL)
  64.         return FALSE;
  65.  
  66.     if (m_hWnd)
  67.         Destroy();
  68.  
  69.     if (pcs == NULL)
  70.         return FALSE;
  71.  
  72.     if (pcs->dwSize != sizeof(FLEXLISTBOXCREATESTRUCT))
  73.         return FALSE;
  74.  
  75.     m_hWndNotify = pcs->hWndNotify ? pcs->hWndNotify : pcs->hWndParent;
  76.  
  77.     m_dwFlags = pcs->dwFlags;
  78.  
  79.     SetFont(pcs->hFont);
  80.     SetColors(pcs->rgbText, pcs->rgbBk, pcs->rgbSelText, pcs->rgbSelBk, pcs->rgbFill, pcs->rgbLine);
  81.     m_VertSB.SetColors(pcs->rgbBk, pcs->rgbFill, pcs->rgbLine);
  82.     m_nSBWidth = pcs->nSBWidth;
  83.  
  84.     if (!CFlexWnd::Create(pcs->hWndParent, pcs->rect, FALSE))
  85.         return FALSE;
  86.  
  87.     FLEXSCROLLBARCREATESTRUCT sbcs;
  88.     sbcs.dwSize = sizeof(FLEXSCROLLBARCREATESTRUCT);
  89.     sbcs.dwFlags = FSBF_VERT;
  90.     sbcs.min = 0;
  91.     sbcs.max = 3;
  92.     sbcs.page = 1;
  93.     sbcs.pos = 1;
  94.     sbcs.hWndParent = m_hWnd;
  95.     sbcs.hWndNotify = m_hWnd;
  96.     RECT rect = {0, 0, 1, 1};
  97.     sbcs.rect = rect;
  98.     sbcs.bVisible = FALSE;
  99.     m_VertSB.Create(&sbcs);
  100.  
  101.     Calc();
  102.  
  103.     // show if we want it shown
  104.     if (pcs->bVisible)
  105.         ShowWindow(m_hWnd, SW_SHOW);
  106.     if (m_bVertSB)
  107.         SetVertSB();
  108.  
  109.     // TODO:  make sure that creation sends no notifications.
  110.     // all initial notifications should be sent here.
  111.  
  112.     return TRUE;
  113. }
  114.  
  115. void CFlexListBox::Calc()
  116. {
  117.     // handle getting text height
  118.     if (m_nTextHeight == -1)
  119.     {
  120.         m_nTextHeight = GetTextHeight(m_hFont);
  121.         Invalidate();
  122.         assert(m_nTextHeight != -1);
  123.     }
  124.  
  125.     // don't do the rest unless we've been created
  126.     if (m_hWnd == NULL)
  127.         return;
  128.  
  129.     // handle integral height
  130.     int iUsedHeight = m_ItemArray.GetSize() * m_nTextHeight;
  131.     // If more than max height, use the max height
  132.     if (iUsedHeight > g_UserNamesRect.bottom - g_UserNamesRect.top)
  133.         iUsedHeight = g_UserNamesRect.bottom - g_UserNamesRect.top;
  134.  
  135.     SIZE client = GetClientSize();
  136.     int fit = iUsedHeight / m_nTextHeight;
  137.     if (fit < 1)
  138.         fit = 1;
  139.     int setheight = (m_dwFlags & FLBF_INTEGRALHEIGHT) ? fit * m_nTextHeight : iUsedHeight;
  140.     if (setheight != client.cy)
  141.         SetWindowPos(m_hWnd, NULL, 0, 0, client.cx, setheight,
  142.             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
  143.  
  144.     // handle scroll bar
  145.     SetVertSB(m_ItemArray.GetSize() > fit);
  146. }
  147.  
  148. void CFlexListBox::SetFont(HFONT hFont)
  149. {
  150.     m_hFont = hFont;
  151.     m_nTextHeight = -1;
  152.     Calc();
  153. }
  154.  
  155. void CFlexListBox::OnPaint(HDC hDC)
  156. {
  157.     HDC hBDC = NULL, hODC = NULL;
  158.     CBitmap *pbm = NULL;
  159.  
  160.     if (!InRenderMode())
  161.     {
  162.         hODC = hDC;
  163.         pbm = CBitmap::Create(GetClientSize(), RGB(0,0,0), hDC);
  164.         if (pbm != NULL)
  165.         {
  166.             hBDC = pbm->BeginPaintInto();
  167.             if (hBDC != NULL)
  168.             {
  169.                 hDC = hBDC;
  170.             }
  171.         }
  172.     }
  173.  
  174.     InternalPaint(hDC);
  175.  
  176.     if (!InRenderMode())
  177.     {
  178.         if (pbm != NULL)
  179.         {
  180.             if (hBDC != NULL)
  181.             {
  182.                 pbm->EndPaintInto(hBDC);
  183.                 pbm->Draw(hODC);
  184.             }
  185.             delete pbm;
  186.         }
  187.     }
  188. }
  189.  
  190. void CFlexListBox::InternalPaint(HDC hDC)
  191. {
  192.     if (m_nTextHeight == -1)
  193.         return;
  194.  
  195.     SIZE client = GetClientSize();
  196.     RECT rc = {0,0,0,0};
  197.     GetClientRect(&rc);
  198.  
  199.     HGDIOBJ hPen, hOldPen, hBrush, hOldBrush;
  200.     hPen= (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbBk);
  201.     if (hPen != NULL)
  202.     {
  203.         hOldPen = SelectObject(hDC, hPen);
  204.  
  205.         hBrush = CreateSolidBrush(m_rgbBk);
  206.         if (hBrush != NULL)
  207.         {
  208.             hOldBrush = SelectObject(hDC, hBrush);
  209.  
  210.             Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);  // Paint entire window black first.
  211.  
  212.             if (!m_bVertSB)
  213.                 m_nTopIndex = 0;
  214.  
  215.             int iLastY;
  216.             for (int at = 0, i = m_nTopIndex; at < client.cy; i++, at += m_nTextHeight)
  217.             {
  218.                 RECT rect = {0, at, client.cx, at + m_nTextHeight};
  219.  
  220.                 if (i < m_ItemArray.GetSize())
  221.                 {
  222.                     BOOL bSel = m_ItemArray[i].bSelected;
  223.                     SetTextColor(hDC, bSel ? m_rgbSelText : m_rgbText);
  224.                     SetBkColor(hDC, bSel ? m_rgbSelBk : m_rgbBk);
  225.                     DrawText(hDC, m_ItemArray[i].GetText(), -1, &rect, DT_NOPREFIX);
  226.                     iLastY = at + m_nTextHeight;
  227.                 }
  228.             }
  229.  
  230.             SelectObject(hDC, hOldBrush);
  231.             DeleteObject(hBrush);
  232.         }
  233.  
  234.         SelectObject(hDC, hOldPen);
  235.         DeleteObject(hPen);
  236.     }
  237.  
  238.     // Draw an outline around the box
  239.     hPen = (HGDIOBJ)CreatePen(PS_SOLID, 1, m_rgbLine);
  240.     if (hPen != NULL)
  241.     {
  242.         hOldPen = SelectObject(hDC, hPen);
  243.         hOldBrush = SelectObject(hDC, GetStockObject(NULL_BRUSH));
  244.         Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
  245.  
  246.         SelectObject(hDC, hOldPen);
  247.         DeleteObject(hPen);
  248.     }
  249. }
  250.  
  251. int CFlexListBox::AddString(LPCTSTR str)
  252. {
  253.     int newIndex = m_ItemArray.GetSize();
  254.     m_ItemArray.SetSize(newIndex + 1);
  255.     FLEXLISTBOXITEM &i = m_ItemArray[newIndex];
  256.     i.SetText(str);
  257.     i.bSelected = FALSE;
  258.  
  259.     SetSBValues();
  260.     Calc();
  261.     Invalidate();
  262.  
  263.     return newIndex;
  264. }
  265.  
  266. void CFlexListBox::StartSel()
  267. {
  268.     if (m_bDragging)
  269.         return;
  270.     SetTimer(m_hWnd, 5, 200, NULL);
  271.     m_bOpenClick = TRUE;  // Initial click on the combobox
  272.     m_bDragging = TRUE;
  273.     m_bCapture = TRUE;
  274.     SetCapture();
  275. }
  276.  
  277. void CFlexListBox::OnWheel(POINT point, WPARAM wParam)
  278. {
  279.     if (!m_bVertSB) return;
  280.  
  281.     int nPage = MulDiv(m_VertSB.GetPage(), 9, 10) >> 1;  // Half a page at a time
  282.  
  283.     if ((int)wParam >= 0)
  284.         m_VertSB.AdjustPos(-nPage);
  285.     else
  286.         m_VertSB.AdjustPos(nPage);
  287.  
  288.     m_nTopIndex = m_VertSB.GetPos();
  289.     if (m_nTopIndex < 0)
  290.         m_nTopIndex = 0;
  291.     Invalidate();
  292. }
  293.  
  294. LRESULT CFlexListBox::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  295. {
  296.     // first handle scroll bar messages
  297.     switch (msg)
  298.     {
  299.         case WM_FLEXVSCROLL:
  300.             int code = (int)wParam;
  301.             CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
  302.             if (!pSB)
  303.                 return 0;
  304.  
  305.             int nLine = 1;
  306.             int nPage = MulDiv(pSB->GetPage(), 9, 10);
  307.  
  308.             switch (code)
  309.             {
  310.                 case SB_LINEUP: pSB->AdjustPos(-nLine); break;
  311.                 case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
  312.                 case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
  313.                 case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
  314.                 case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
  315.                 case SB_ENDSCROLL:
  316.                     SetCapture();     // Recapture after the scroll bar releases the capture.
  317.                     break;
  318.             }
  319.  
  320.             switch (msg)
  321.             {
  322.                 case WM_FLEXVSCROLL:
  323.                     m_nTopIndex = pSB->GetPos();
  324.                     if (m_nTopIndex < 0)
  325.                         m_nTopIndex = 0;
  326.                     break;
  327.             }
  328.  
  329.             Invalidate();
  330.             return 0;
  331.     }
  332.  
  333.     // now non-scrolly input
  334.     switch (msg)
  335.     {
  336.         case WM_SIZE:
  337.             SetSBValues();
  338.             Calc();
  339.             Invalidate();
  340.             return 0;
  341.  
  342.         // make sure flexwnd doesn't do ANYTHING with our mouse messages
  343.         case WM_MOUSEMOVE:
  344.         case WM_LBUTTONUP:
  345.         case WM_LBUTTONDOWN:
  346.         case WM_RBUTTONUP:
  347.         case WM_RBUTTONDOWN:
  348.         case WM_LBUTTONDBLCLK:
  349.         case WM_RBUTTONDBLCLK:
  350.         {
  351.             POINT point = {int(signed short(LOWORD(lParam))), int(signed short(HIWORD(lParam)))};
  352.             m_point = point;
  353.         }
  354.         case WM_TIMER:
  355.         case WM_CAPTURECHANGED:
  356.             break;
  357.         default:
  358.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  359.     }
  360.  
  361.     switch (msg)
  362.     {
  363.         case WM_LBUTTONDOWN:
  364.             // Check if we clicked the scroll bar area.  If so, send the click to the scroll bar.
  365.             RECT rc;
  366.             m_VertSB.GetClientRect(&rc);
  367.             ClientToScreen(m_VertSB.m_hWnd, &rc);
  368.             ScreenToClient(m_hWnd, &rc);
  369.             if (PtInRect(&rc, m_point))
  370.             {
  371.                 POINT point = {int(signed short(LOWORD(lParam))), int(signed short(HIWORD(lParam)))};
  372.                 ClientToScreen(m_hWnd, &point);
  373.                 ScreenToClient(m_VertSB.m_hWnd, &point);
  374.                 PostMessage(m_VertSB.m_hWnd, WM_LBUTTONDOWN, wParam, point.x + (point.y << 16));  // This will make it lose capture.
  375.             } else
  376.                 StartSel();
  377.             break;
  378.  
  379.         case WM_MOUSEMOVE:
  380.         case WM_LBUTTONUP:
  381.             if (!m_bDragging)
  382.                 break;
  383.             if (m_nTextHeight == -1)
  384.                 break;
  385.         case WM_TIMER:
  386.             if (m_bDragging || msg != WM_TIMER)
  387.             {
  388.                 int adj = m_point.y < 0 ? -1 : 0;
  389.                 SelectAndShowSingleItem(adj + m_point.y / m_nTextHeight + m_nTopIndex, msg != WM_MOUSEMOVE);
  390.                 Notify(FLBN_SEL);
  391.             }
  392.             // Check if the mouse cursor is within the listbox rectangle.  If not, don't show the tooltip.
  393.             if (msg == WM_MOUSEMOVE)
  394.             {
  395.                 RECT rect;
  396.                 GetClientRect(&rect);
  397.                 POINT pt;
  398.                 GetCursorPos(&pt);
  399.                 ScreenToClient(m_hWnd, &pt);
  400.                 if (!PtInRect(&rect, pt))
  401.                 {
  402.                     CFlexWnd::s_ToolTip.SetToolTipParent(NULL);
  403.                     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  404.                 }
  405.             }
  406.             break;
  407.     }
  408.  
  409.     switch (msg)
  410.     {
  411.         case WM_CAPTURECHANGED:
  412.             if ((HWND)lParam == m_VertSB.m_hWnd)  // If the scroll bar is getting the capture, we do not clean up.
  413.                 break;
  414.         case WM_LBUTTONUP:
  415.             if (m_bOpenClick)
  416.             {
  417.                 m_bOpenClick = FALSE;  // If this is the result of clicking the combobox window, don't release capture.
  418.                 break;
  419.             }
  420.             if (m_bCapture)
  421.             {
  422.                 m_bCapture = FALSE;
  423.                 KillTimer(m_hWnd, 5);
  424.                 ReleaseCapture();
  425.                 m_bDragging = FALSE;
  426.                 BOOL bCancel = TRUE;
  427.                 if (msg == WM_LBUTTONUP)
  428.                 {
  429.                     RECT wrect;
  430.                     GetClientRect(&wrect);
  431.                     if (PtInRect(&wrect, m_point))
  432.                         bCancel = FALSE;
  433.                 }
  434.                 Notify(bCancel ? FLBN_CANCEL : FLBN_FINALSEL);
  435.             }
  436.             break;
  437.     }
  438.  
  439.     return 0;
  440. }
  441.  
  442. void CFlexListBox::SelectAndShowSingleItem(int i, BOOL bScroll)
  443. {
  444.     int nItems = m_ItemArray.GetSize();
  445.  
  446.     if (nItems < 1)
  447.     {
  448.         m_nSelItem = i;  // We have to update m_nSelItem even if there is no items because the username combobox
  449.                          // is not initialized when there is only 1 user.  Selection of user 0 is assumed.
  450.         return;
  451.     }
  452.  
  453.     if (i < 0)
  454.         i = 0;
  455.     if (i >= nItems)
  456.         i = nItems - 1;
  457.  
  458.     if (m_nSelItem >= 0 && m_nSelItem < nItems)
  459.         m_ItemArray[m_nSelItem].bSelected = FALSE;
  460.  
  461.     m_nSelItem = i;
  462.     m_ItemArray[m_nSelItem].bSelected = TRUE;
  463.  
  464.     if (bScroll)
  465.     {
  466.         SIZE client = GetClientSize();
  467.         int nBottomIndex = m_nTopIndex + client.cy / m_nTextHeight - 1;
  468.  
  469.         if (m_nSelItem < m_nTopIndex)
  470.             m_nTopIndex = m_nSelItem;
  471.  
  472.         assert(m_nTopIndex >= 0);
  473.  
  474.         if (m_nSelItem > nBottomIndex)
  475.         {
  476.             m_nTopIndex += m_nSelItem - nBottomIndex + 1;
  477.             nBottomIndex = m_nSelItem + 1;
  478.         }
  479.  
  480.         if (nBottomIndex > nItems - 1)
  481.             m_nTopIndex -= nBottomIndex - nItems + 1;
  482.  
  483.         if (m_nTopIndex < 0)
  484.             m_nTopIndex = 0;
  485.  
  486.         if (m_nTopIndex >= nItems)
  487.             m_nTopIndex = nItems - 1;
  488.  
  489.         assert(m_nTopIndex >= 0 && m_nTopIndex < nItems);
  490.     }
  491.  
  492.     if (m_bVertSB)
  493.         SetSBValues();
  494.  
  495.     SIZE client = GetClientSize();
  496.     int nBottomIndex = m_nTopIndex + client.cy / m_nTextHeight - 1;
  497.     int iToolTipIndex = m_nSelItem;
  498.     // Make sure that we don't display tooltip for items outside the listbox window
  499.     if (iToolTipIndex > nBottomIndex)
  500.         iToolTipIndex = nBottomIndex;
  501.     if (iToolTipIndex < m_nTopIndex)
  502.         iToolTipIndex = m_nTopIndex;
  503.     // Create and initialize a tooltip if the text is too long to fit.
  504.     RECT rect = {0, 0, client.cx, m_nTextHeight};
  505.     RECT ResultRect = rect;
  506.     HDC hDC = CreateCompatibleDC(NULL);
  507.     if (hDC)
  508.     {
  509.         DrawText(hDC, m_ItemArray[iToolTipIndex].GetText(), -1, &ResultRect, DT_NOPREFIX|DT_CALCRECT);
  510.         DeleteDC(hDC);
  511.     }
  512.     if (ResultRect.right > rect.right)
  513.     {
  514.         TOOLTIPINITPARAM ttip;
  515.         ttip.hWndParent = GetParent(m_hWnd);
  516.         ttip.iSBWidth = m_nSBWidth;
  517.         ttip.dwID = iToolTipIndex;
  518.         ttip.hWndNotify = m_hWnd;
  519.         ttip.tszCaption = GetSelText();
  520.         CFlexToolTip::UpdateToolTipParam(ttip);
  521.     }
  522.  
  523.     Invalidate();
  524. }
  525.  
  526. void CFlexListBox::Notify(int code)
  527. {
  528.     if (!m_hWndNotify)
  529.         return;
  530.  
  531.     SendMessage(m_hWndNotify, WM_FLEXLISTBOX,
  532.         (WPARAM)code, (LPARAM)(LPVOID)this);
  533. }
  534.  
  535. void CFlexListBox::SetColors(COLORREF text, COLORREF bk, COLORREF seltext, COLORREF selbk, COLORREF fill, COLORREF line)
  536. {
  537.     m_rgbText = text;
  538.     m_rgbBk = bk;
  539.     m_rgbSelText = seltext;
  540.     m_rgbSelBk = selbk;
  541.     m_rgbFill = fill;
  542.     m_rgbLine = line;
  543.     Invalidate();
  544. }
  545.  
  546. void CFlexListBox::SetVertSB(BOOL bSet)
  547. {
  548.     if (bEq(bSet, m_bVertSB))
  549.         return;
  550.  
  551.     m_bVertSB = bSet;
  552.  
  553.     if (m_hWnd == NULL)
  554.         return;
  555.  
  556.     SetVertSB();
  557. }
  558.  
  559. void CFlexListBox::SetVertSB()
  560. {
  561.     if (m_bVertSB)
  562.     {
  563.         SetSBValues();
  564.         SIZE client = GetClientSize();
  565.         SetWindowPos(m_VertSB.m_hWnd, NULL, client.cx - m_nSBWidth - 1, 0, m_nSBWidth, client.cy - 1, SWP_NOZORDER);
  566.     }
  567.  
  568.     ShowWindow(m_VertSB.m_hWnd, m_bVertSB ? SW_SHOW : SW_HIDE);
  569. }
  570.  
  571. void CFlexListBox::SetSBValues()
  572. {
  573.     if (m_hWnd == NULL)
  574.         return;
  575.  
  576.     SIZE client = GetClientSize();
  577.     int fit = client.cy / m_nTextHeight;
  578.     m_VertSB.SetValues(0, m_ItemArray.GetSize(), fit, m_nTopIndex);
  579. }
  580.  
  581. LPCTSTR CFlexListBox::GetSelText()
  582. {
  583.     if (m_nSelItem < 0 || m_nSelItem >= m_ItemArray.GetSize())
  584.         return NULL;
  585.  
  586.     return m_ItemArray[m_nSelItem].GetText();
  587. }
  588.  
  589. int CFlexListBox::GetSel()
  590. {
  591.     return m_nSelItem;
  592. }
  593.